package io.gitee.zhangbinhub.admin.service

import io.gitee.zhangbinhub.acp.boot.exceptions.WebException
import io.gitee.zhangbinhub.acp.boot.log.LogAdapter
import io.gitee.zhangbinhub.acp.core.common.CommonTools
import io.gitee.zhangbinhub.admin.base.OauthBaseService
import io.gitee.zhangbinhub.admin.component.WorkFlowListener
import io.gitee.zhangbinhub.admin.constant.WorkFlowParamKey
import io.gitee.zhangbinhub.admin.entity.MyProcessInstance
import io.gitee.zhangbinhub.admin.po.ProcessHandlingPo
import io.gitee.zhangbinhub.admin.po.ProcessQueryPo
import io.gitee.zhangbinhub.admin.po.ProcessStartPo
import io.gitee.zhangbinhub.admin.po.ProcessTerminationPo
import io.gitee.zhangbinhub.admin.repo.MyProcessInstanceRepository
import io.gitee.zhangbinhub.admin.repo.UserRepository
import io.gitee.zhangbinhub.admin.vo.*
import jakarta.persistence.criteria.Predicate
import org.camunda.bpm.engine.*
import org.camunda.bpm.engine.history.HistoricActivityInstance
import org.camunda.bpm.engine.history.HistoricProcessInstance
import org.camunda.bpm.engine.history.HistoricVariableUpdate
import org.camunda.bpm.engine.runtime.ProcessInstance
import org.camunda.bpm.engine.task.DelegationState
import org.camunda.bpm.engine.task.Task
import org.camunda.bpm.model.bpmn.Bpmn
import org.camunda.bpm.model.bpmn.instance.BaseElement
import org.camunda.bpm.model.bpmn.instance.SequenceFlow
import org.camunda.bpm.model.bpmn.instance.camunda.CamundaProperties
import org.joda.time.DateTime
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.stereotype.Service
import org.springframework.transaction.annotation.Transactional

/**
 * @author zhang by 10/06/2019
 * @since JDK 11
 */
@Service
@Transactional(readOnly = true)
class WorkFlowService @Autowired constructor(
    private val logAdapter: LogAdapter,
    private val workFlowListener: WorkFlowListener,
    userRepository: UserRepository,
    private val formService: FormService,
    private val identityService: IdentityService,
    private val runtimeService: RuntimeService,
    private val taskService: TaskService,
    private val repositoryService: RepositoryService,
    private val historyService: HistoryService,
    private val myProcessInstanceRepository: MyProcessInstanceRepository
) : OauthBaseService(userRepository) {
    private val anonymousUserId = "anonymousUser"

    @Throws(WebException::class)
    private fun getUserById(id: String?): UserVo = if (CommonTools.isNullStr(id) || id == anonymousUserId) {
        UserVo()
    } else {
        userRepository.findById(id!!).let {
            if (it.isEmpty) throw WebException("找不到用户信息")
            formatUserVo(it.get())
        }
    }

    private fun getExtensionProperties(processDefinitionId: String, elementId: String): MutableMap<String, String> =
        repositoryService.getBpmnModelInstance(processDefinitionId)
            .getModelElementById<BaseElement>(elementId)?.extensionElements?.elementsQuery?.filterByType(
                CamundaProperties::class.java
            )?.list()?.flatMap { it.camundaProperties }?.associate { it.camundaName to it.camundaValue }?.toMutableMap()
            ?: mutableMapOf()

    /**
     * 获取待办人员ID
     */
    private fun getPendingUserId(task: Task): List<String> =
        if (CommonTools.isNullStr(task.assignee)) {
            workFlowListener.getIdentityLinkUserId(taskService.getIdentityLinksForTask(task.id))
        } else {
            listOf(task.assignee)
        }


    /**
     * 获取待办人员
     */
    private fun getPendingUser(task: Task): List<UserVo> =
        userRepository.findAllById(getPendingUserId(task)).map { item -> formatUserVo(item) }

    @Throws(WebException::class)
    private fun findRuntimeProcessInstance(processInstanceId: String): ProcessInstance = try {
        runtimeService.createProcessInstanceQuery().processInstanceId(processInstanceId)
            .singleResult() ?: throw WebException("流程实例不存在！")
    } catch (e: Exception) {
        logAdapter.error(e.message, e)
        throw WebException(e.message)
    }

    /**
     * 任务实体转换
     *
     * @param task 任务对象
     * @return 转换后任务对象
     */
    @Throws(WebException::class)
    private fun taskToVo(task: Task) =
        historyService.createHistoricProcessInstanceQuery().processInstanceId(task.processInstanceId)
            .singleResult()?.let { processInstance ->
                val params = runtimeService.createVariableInstanceQuery()
                    .processInstanceIdIn(task.processInstanceId).list()
                    .associate {
                        it.name to it.value
                    }.toMutableMap()
                ProcessTaskVo(
                    processInstanceId = task.processInstanceId,
                    name = task.name,
                    taskId = task.id,
                    taskDefinitionKey = task.taskDefinitionKey,
                    parentTaskId = task.parentTaskId,
                    executionId = task.executionId,
                    params = params,
                    businessKey = processInstance.businessKey ?: "",
                    unClaimed = task.assignee == null,
                    user = getUserById(task.assignee),
                    localParams = runtimeService.getVariablesLocal(task.executionId).toMutableMap(),
                    formFields = formService.getTaskFormData(task.id).formFields
                        .associate { it.label to it.value.value }.toMutableMap(),
                    properties = getExtensionProperties(processInstance.processDefinitionId, task.taskDefinitionKey),
                    createTime = task.createTime.time,
                    processDefinitionKey = processInstance.processDefinitionKey,
                    flowName = processInstance.processDefinitionName,
                    title = params[WorkFlowParamKey.title]?.toString() ?: "",
                    description = params[WorkFlowParamKey.description]?.toString() ?: "",
                    startUser = getUserById(processInstance.startUserId),
                    taskOwnerUser = getUserById(task.owner),
                    delegated = task.delegationState == DelegationState.PENDING,
                    pendingUserList = getPendingUser(task)
                )
            } ?: throw WebException("获取流程实例失败")

    /**
     * 历史记录实例转换
     *
     * @param historicActivityInstance 历史记录
     * @return 转换后对象
     */
    private fun actToVo(
        historicActivityInstance: HistoricActivityInstance, businessKey: String
    ): ProcessHistoryActivityVo =
        historyService.createHistoricDetailQuery().activityInstanceId(historicActivityInstance.id).variableUpdates()
            .orderByTime().asc().list().let { historicDetailList ->
                val params: MutableMap<String, Any> = mutableMapOf()
                historicDetailList.forEach { historicDetail ->
                    val name = (historicDetail as HistoricVariableUpdate).variableName
                    if (params.containsKey(name) && WorkFlowParamKey.comment == name) {
                        params[name] = "${params[name].toString()}\n------\n${historicDetail.value}"
                    } else {
                        params[name] = historicDetail.value
                    }
                }
                historyService.createHistoricTaskInstanceQuery().taskId(historicActivityInstance.taskId).singleResult()
                    ?.let {
                        ProcessHistoryActivityVo(
                            processInstanceId = historicActivityInstance.processInstanceId,
                            activityId = historicActivityInstance.activityId,
                            activityName = historicActivityInstance.activityName,
                            taskId = historicActivityInstance.taskId,
                            taskDefinitionKey = it.taskDefinitionKey,
                            executionId = historicActivityInstance.executionId,
                            businessKey = businessKey,
                            user = getUserById(historicActivityInstance.assignee),
                            pass = params[WorkFlowParamKey.pass]?.let { pass -> pass as Boolean },
                            comment = params[WorkFlowParamKey.comment]?.toString(),
                            params = params,
                            startTime = historicActivityInstance.startTime.time,
                            endTime = historicActivityInstance.endTime?.time
                        )
                    } ?: throw WebException("获取任务信息失败！")
            }

    /**
     * 流程实例转换
     * @param processInstance 流程实例
     * @return 转换后的对象
     */
    @Throws(WebException::class)
    private fun instanceToVo(processInstance: HistoricProcessInstance): ProcessInstanceVo {
        val params = historyService.createHistoricVariableInstanceQuery()
            .processInstanceIdIn(processInstance.id).list()
            .associate {
                it.name to it.value
            }.toMutableMap()
        return ProcessInstanceVo(
            processInstanceId = processInstance.id,
            finished = processInstance.endTime != null,
            processDefinitionKey = processInstance.processDefinitionKey,
            businessKey = processInstance.businessKey,
            flowName = processInstance.processDefinitionName,
            title = params[WorkFlowParamKey.title]?.toString() ?: "",
            description = params[WorkFlowParamKey.description]?.toString() ?: "",
            startUser = getUserById(processInstance.startUserId),
            activityUser = taskService.createTaskQuery().processInstanceId(processInstance.id).list()
                .flatMap { task ->
                    getPendingUser(task)
                }.toMutableList(),
            params = params,
            startTime = processInstance.startTime!!.time,
            endTime = processInstance.endTime?.time,
            deleteReason = processInstance.deleteReason
        )
    }

    @Throws(WebException::class)
    private fun getTask(taskId: String): Task = taskService.createTaskQuery().taskId(taskId).singleResult() ?: run {
        logAdapter.error("流程任务【$taskId】不存在！")
        throw WebException("流程任务不存在！")
    }

    /**
     * 启动流程
     *
     * @param processStartPo 流程启动参数
     * @param userId 流程发起人ID
     * @return 流程实例id
     */
    @Transactional
    @Throws(WebException::class)
    fun startFlow(processStartPo: ProcessStartPo, userId: String? = null): String = try {
        val params = processStartPo.params
        repositoryService.createProcessDefinitionQuery().processDefinitionKey(processStartPo.processDefinitionKey)
            .orderByProcessDefinitionVersion().desc().list().apply {
                if (this.isEmpty()) {
                    throw WebException("找不到对应的流程定义【${processStartPo.processDefinitionKey}】")
                }
                params[WorkFlowParamKey.flowName] = this[0].name
            }
        params[WorkFlowParamKey.businessKey] = processStartPo.businessKey!!
        params[WorkFlowParamKey.title] = processStartPo.title!!
        params[WorkFlowParamKey.description] = processStartPo.description!!
        userId?.apply {
            params[WorkFlowParamKey.startUserId] = this
            identityService.setAuthenticatedUserId(this)
        }
        runtimeService.startProcessInstanceByKey(
            processStartPo.processDefinitionKey, processStartPo.businessKey, params
        ).id.also {
            logAdapter.info(
                "流程启动成功：流程定义【${processStartPo.processDefinitionKey}】流程实例ID【$it】业务KEY【${processStartPo.businessKey}】启动人【${
                    getUserById(
                        userId
                    ).loginNo
                }】"
            )
        }
    } catch (e: Exception) {
        logAdapter.error(e.message, e)
        throw WebException(e.message)
    }

    /**
     * 获取任务信息
     *
     * @param taskId 任务ID
     * @return 任务信息
     */
    @Throws(WebException::class)
    fun findTaskById(loginNo: String, taskId: String): ProcessTaskVo = try {
        getUserInfoByLoginNo(loginNo)?.let { userInfo ->
            taskService.createTaskQuery().or()
                .taskAssignee(userInfo.id)
                .taskCandidateUser(userInfo.id).includeAssignedTasks()
                .taskCandidateGroupIn(userInfo.roleSet.map { role -> role.code }.toList()).includeAssignedTasks()
                .endOr()
                .taskId(taskId).singleResult()?.let {
                    taskToVo(it)
                } ?: throw WebException("找不到信息")
        } ?: throw WebException("获取当前登录用户信息失败！")
    } catch (e: Exception) {
        logAdapter.error(e.message, e)
        throw WebException(e.message)
    }

    /**
     * 获取任务信息（开放的）
     *
     * @param taskId 任务ID
     * @return 任务信息
     */
    @Throws(WebException::class)
    fun findTaskByIdOpen(taskId: String): ProcessTaskVo = try {
        taskToVo(getTask(taskId))
    } catch (e: Exception) {
        logAdapter.error(e.message, e)
        throw WebException(e.message)
    }

    /**
     * 获取待办任务
     * @param processInstanceId 流程实例ID
     * @param userId 用户ID
     */
    @Throws(WebException::class)
    fun findTaskList(processInstanceId: String, userId: String? = null): List<ProcessTaskVo> = try {
        taskService.createTaskQuery().processInstanceId(processInstanceId).apply {
            if (!CommonTools.isNullStr(userId)) {
                this.taskAssignee(userId)
            }
        }.list().map {
            taskToVo(it)
        }
    } catch (e: Exception) {
        logAdapter.error(e.message, e)
        throw WebException(e.message)
    }

    /**
     * 获取待办任务
     *
     * @return 任务列表
     */
    @Throws(WebException::class)
    fun findTaskList(loginNo: String, processQueryPo: ProcessQueryPo): CustomerQueryPageVo<ProcessTaskVo> = try {
        val firstResult = (processQueryPo.queryParam!!.currPage!! - 1) * processQueryPo.queryParam!!.pageSize!!
        val maxResult = processQueryPo.queryParam!!.pageSize!!
        getUserInfoByLoginNo(loginNo)?.let { userInfo ->
            val taskQuery = taskService.createTaskQuery().or()
                .taskAssignee(userInfo.id)
                .taskCandidateUser(userInfo.id).includeAssignedTasks()
                .taskCandidateGroupIn(userInfo.roleSet.map { role -> role.code }.toList()).includeAssignedTasks()
                .endOr()
            if (processQueryPo.processDefinitionKeys != null && processQueryPo.processDefinitionKeys!!.isNotEmpty()) {
                taskQuery.processDefinitionKeyIn(*processQueryPo.processDefinitionKeys!!.toTypedArray())
            }
            if (processQueryPo.processInstanceIds != null && processQueryPo.processInstanceIds!!.isNotEmpty()) {
                taskQuery.processInstanceIdIn(*processQueryPo.processInstanceIds!!.toTypedArray())
            }
            if (!CommonTools.isNullStr(processQueryPo.processBusinessKey)) {
                taskQuery.processInstanceBusinessKeyLike(processQueryPo.processBusinessKey)
            }
            if (processQueryPo.startTime != null) {
                taskQuery.taskCreatedAfter(DateTime(processQueryPo.startTime!!).toDate())
            }
            if (processQueryPo.endTime != null) {
                taskQuery.taskCreatedBefore(DateTime(processQueryPo.endTime!!).toDate())
            }
            val total = taskQuery.list().count()
            val list = taskQuery.orderByTaskCreateTime().asc()
                .listPage(firstResult, maxResult).map { task -> taskToVo(task) }
            CustomerQueryPageVo(
                currPage = processQueryPo.queryParam!!.currPage!!,
                pageSize = processQueryPo.queryParam!!.pageSize!!,
                totalElements = total.toLong(),
                content = list
            )
        } ?: throw WebException("获取当前登录用户信息失败！")
    } catch (e: Exception) {
        logAdapter.error(e.message, e)
        throw WebException(e.message)
    }

    /**
     * 签收任务
     *
     * @param taskId 任务id
     */
    @Transactional
    @Throws(WebException::class)
    fun claimTask(loginNo: String, taskId: String, userId: String? = null) {
        try {
            (if (CommonTools.isNullStr(userId)) {
                getUserInfoByLoginNo(loginNo)?.id ?: throw WebException("获取当前登录用户信息失败！")
            } else {
                userId!!
            }).let { targetUserId ->
                identityService.setAuthenticatedUserId(targetUserId)
                val task = getTask(taskId)
                val processInstance = findRuntimeProcessInstance(task.processInstanceId)
                workFlowListener.notifyPendingFinished(processInstance.businessKey, task, getPendingUserId(task))
                taskService.claim(taskId, targetUserId)
                workFlowListener.notifyPendingCreated(processInstance.businessKey, task, listOf(targetUserId))
                logAdapter.info(
                    "任务签收成功：流程实例ID【${task.processInstanceId}】任务ID【$taskId】签收人【${
                        getUserById(
                            targetUserId
                        ).loginNo
                    }】"
                )
            }
        } catch (e: Exception) {
            logAdapter.error(e.message, e)
            throw WebException(e.message)
        }
    }

    /**
     * 转办任务
     *
     * @param taskId 任务id
     */
    @Transactional
    @Throws(WebException::class)
    fun turnTask(loginNo: String, taskId: String, acceptUserId: String) {
        try {
            getUserInfoByLoginNo(loginNo)?.let { userInfo ->
                identityService.setAuthenticatedUserId(userInfo.id)
                val task = getTask(taskId)
                val processInstance = findRuntimeProcessInstance(task.processInstanceId)
                workFlowListener.notifyPendingFinished(processInstance.businessKey, task, getPendingUserId(task))
                taskService.setAssignee(taskId, acceptUserId)
                workFlowListener.notifyPendingCreated(processInstance.businessKey, task, listOf(acceptUserId))
                logAdapter.info(
                    "任务转办成功：流程实例ID【${task.processInstanceId}】任务ID【$taskId】操作人【${userInfo.loginNo}】新办理人【${
                        getUserById(
                            acceptUserId
                        ).loginNo
                    }】"
                )
            } ?: throw WebException("获取当前登录用户信息失败！")
        } catch (e: Exception) {
            logAdapter.error(e.message, e)
            throw WebException(e.message)
        }
    }

    /**
     * 委派任务
     *
     * @param taskId 任务id
     */
    @Transactional
    @Throws(WebException::class)
    fun delegateTask(loginNo: String, taskId: String, acceptUserId: String) {
        try {
            getUserInfoByLoginNo(loginNo)?.let { userInfo ->
                identityService.setAuthenticatedUserId(userInfo.id)
                val task = taskService.createTaskQuery().taskId(taskId).singleResult()
                if (task == null) {
                    logAdapter.error("流程任务【$taskId】不存在！")
                    throw WebException("流程任务不存在！")
                }
                taskService.setOwner(taskId, userInfo.id)
                val processInstance = findRuntimeProcessInstance(task.processInstanceId)
                workFlowListener.notifyPendingFinished(processInstance.businessKey, task, listOf(userInfo.id))
                taskService.delegateTask(taskId, acceptUserId)
                workFlowListener.notifyPendingCreated(processInstance.businessKey, task, listOf(acceptUserId))
                logAdapter.info(
                    "任务委派成功：流程实例ID【${task.processInstanceId}】任务ID【$taskId】操作人【${userInfo.loginNo}】新办理人【${
                        getUserById(
                            acceptUserId
                        ).loginNo
                    }】"
                )
            } ?: throw WebException("获取当前登录用户信息失败！")
        } catch (e: Exception) {
            logAdapter.error(e.message, e)
            throw WebException(e.message)
        }
    }

    /**
     * 任务处理
     *
     * @param processHandlingPo 任务处理参数
     */
    @Transactional
    @Throws(WebException::class)
    fun processTask(processHandlingPo: ProcessHandlingPo, userId: String) {
        try {
            identityService.setAuthenticatedUserId(userId)
            val task = taskService.createTaskQuery().taskId(processHandlingPo.taskId).singleResult()
            if (task == null) {
                logAdapter.error("流程任务【${processHandlingPo.taskId}】不存在！")
                throw WebException("流程任务不存在！")
            }
            val params: MutableMap<String, Any> = mutableMapOf()
            processHandlingPo.params.forEach { (key, value) ->
                if (!params.containsKey(key)) {
                    params[key] = value
                }
            }
            val comment = if (processHandlingPo.pass!!) {
                if (CommonTools.isNullStr(processHandlingPo.comment)) {
                    "通过"
                } else {
                    processHandlingPo.comment!!
                }
            } else {
                if (CommonTools.isNullStr(processHandlingPo.comment)) {
                    "不通过"
                } else {
                    processHandlingPo.comment!!
                }
            }
            params[WorkFlowParamKey.comment] = if (task.delegationState == DelegationState.PENDING) {
                (getUserById(userId).name ?: "") + ":" + comment
            } else {
                comment
            }
            params[WorkFlowParamKey.pass] = processHandlingPo.pass!!
            runtimeService.setVariablesLocal(task.executionId, processHandlingPo.localParams)
            val processInstance = findRuntimeProcessInstance(task.processInstanceId)
            if (task.delegationState == DelegationState.PENDING) {
                workFlowListener.notifyPendingFinished(processInstance.businessKey, task, listOf(userId))
                taskService.resolveTask(task.id, params)
                workFlowListener.notifyPendingCreated(processInstance.businessKey, task, listOf(task.owner))
            } else {
                workFlowListener.notifyPendingFinished(processInstance.businessKey, task, listOf(userId))
                taskService.setAssignee(task.id, userId)
                taskService.complete(task.id, params)
            }
            logAdapter.info(
                "任务处理完成：流程实例ID【${task.processInstanceId}】任务ID【${processHandlingPo.taskId}】操作人【${
                    getUserById(
                        userId
                    ).loginNo
                }】意见【$comment】"
            )
            // 添加至我处理过的流程实例
            myProcessInstanceRepository.findByUserIdAndProcessInstanceId(userId, task.processInstanceId).let {
                if (!it.isPresent) {
                    val processInstanceVo = findProcessInstance(task.processInstanceId)
                    myProcessInstanceRepository.save(
                        MyProcessInstance(
                            processInstanceId = processInstanceVo.processInstanceId!!,
                            processDefinitionKey = processInstanceVo.processDefinitionKey!!,
                            businessKey = processInstanceVo.businessKey!!,
                            startUserId = processInstanceVo.startUser?.id,
                            userId = userId,
                            startTime = processInstanceVo.startTime
                        )
                    )
                }
            }
        } catch (e: Exception) {
            logAdapter.error(e.message, e)
            throw WebException(e.message)
        }
    }

    @Transactional
    @Throws(WebException::class)
    fun deleteProcessInstance(processTerminationPo: ProcessTerminationPo, userId: String? = null) {
        val processInstance = findRuntimeProcessInstance(processTerminationPo.processInstanceId!!)
        taskService.createTaskQuery().processInstanceId(processTerminationPo.processInstanceId).list()
            .let { taskList ->
                taskList.forEach { task ->
                    workFlowListener.notifyPendingFinished(processInstance.businessKey, task, getPendingUserId(task))
                }
                runtimeService.deleteProcessInstance(
                    processTerminationPo.processInstanceId, processTerminationPo.reason
                )
                logAdapter.info(
                    "流程删除成功：流程实例ID【${processTerminationPo.processInstanceId}】操作人【${
                        getUserById(
                            userId
                        ).loginNo
                    }】"
                )
            }
    }

    @Throws(WebException::class)
    fun findProcessDefinition(processInstanceId: String): ProcessDefinitionVo = try {
        historyService.createHistoricProcessInstanceQuery().processInstanceId(processInstanceId)
            .singleResult()?.let { instance ->
                repositoryService.getBpmnModelInstance(instance.processDefinitionId)?.let { modelInstance ->
                    ProcessDefinitionVo(
                        id = instance.processDefinitionId,
                        name = instance.processDefinitionName,
                        content = Bpmn.convertToString(modelInstance)
                    )
                }
            } ?: throw WebException("流程实例不存在！")
    } catch (e: Exception) {
        logAdapter.error(e.message, e)
        throw WebException(e.message)
    }

    @Throws(WebException::class)
    fun findProcessInstance(processInstanceId: String): ProcessInstanceVo = try {
        historyService.createHistoricProcessInstanceQuery().processInstanceId(processInstanceId)
            .singleResult()?.let { instance ->
                instanceToVo(instance)
            } ?: throw WebException("流程实例不存在！")
    } catch (e: Exception) {
        logAdapter.error(e.message, e)
        throw WebException(e.message)
    }

    /**
     * 查询流程实例
     */
    @Throws(WebException::class)
    fun findProcessInstance(processQueryPo: ProcessQueryPo): CustomerQueryPageVo<ProcessInstanceVo> = try {
        val firstResult = (processQueryPo.queryParam!!.currPage!! - 1) * processQueryPo.queryParam!!.pageSize!!
        val maxResult = processQueryPo.queryParam!!.pageSize!!
        val processInstanceQuery = historyService.createHistoricProcessInstanceQuery()
        if (processQueryPo.processDefinitionKeys != null && processQueryPo.processDefinitionKeys!!.isNotEmpty()) {
            processInstanceQuery.processDefinitionKeyIn(*processQueryPo.processDefinitionKeys!!.toTypedArray())
        }
        if (processQueryPo.processInstanceIds != null && processQueryPo.processInstanceIds!!.isNotEmpty()) {
            processInstanceQuery.processInstanceIds(processQueryPo.processInstanceIds!!.toSet())
        }
        if (!CommonTools.isNullStr(processQueryPo.processBusinessKey)) {
            processInstanceQuery.processInstanceBusinessKeyLike(processQueryPo.processBusinessKey)
        }
        if (!CommonTools.isNullStr(processQueryPo.startUserId)) {
            processInstanceQuery.startedBy(processQueryPo.startUserId)
        }
        if (processQueryPo.startTime != null) {
            processInstanceQuery.startedAfter(DateTime(processQueryPo.startTime!!).toDate())
        }
        if (processQueryPo.endTime != null) {
            processInstanceQuery.startedBefore(DateTime(processQueryPo.endTime!!).toDate())
        }
        if (processQueryPo.finished != null && processQueryPo.finished!!) {
            processInstanceQuery.finished()
        } else {
            processInstanceQuery.unfinished()
        }
        val total = processInstanceQuery.list().count()
        val list = processInstanceQuery.orderByProcessInstanceStartTime().desc().listPage(firstResult, maxResult)
            .map { instance -> instanceToVo(instance) }
        CustomerQueryPageVo(
            currPage = processQueryPo.queryParam!!.currPage!!,
            pageSize = processQueryPo.queryParam!!.pageSize!!,
            totalElements = total.toLong(),
            content = list
        )
    } catch (e: Exception) {
        logAdapter.error(e.message, e)
        throw WebException(e.message)
    }

    /**
     * 查询指定流程的历史信息
     *
     * @param processInstanceId 流程id
     * @return 流程历史信息
     */
    @Throws(WebException::class)
    fun findHistoryActivity(processInstanceId: String): List<ProcessHistoryActivityVo> = try {
        val historicProcessInstance =
            historyService.createHistoricProcessInstanceQuery().processInstanceId(processInstanceId).singleResult()
        if (historicProcessInstance == null) {
            logAdapter.error("流程实例【$processInstanceId】不存在")
            throw WebException("流程实例不存在！")
        }
        historyService.createHistoricActivityInstanceQuery().processInstanceId(processInstanceId)
            .orderByHistoricActivityInstanceStartTime().asc().list()
            .filter { historicActivityInstance -> !CommonTools.isNullStr(historicActivityInstance.taskId) }
            .map { historicActivityInstance ->
                actToVo(
                    historicActivityInstance, historicProcessInstance.businessKey
                )
            }.filter { processHistoryActivityVo -> !CommonTools.isNullStr(processHistoryActivityVo.comment) }
    } catch (e: Exception) {
        logAdapter.error(e.message, e)
        throw WebException(e.message)
    }

    /**
     * 查询当前用户处理过的流程实例
     */
    @Throws(WebException::class)
    fun findProcessInstanceForMyProcess(
        loginNo: String,
        processQueryPo: ProcessQueryPo
    ): CustomerQueryPageVo<ProcessInstanceVo> = try {
        getUserInfoByLoginNo(loginNo)?.let { userInfo ->
            myProcessInstanceRepository.findAll({ root, _, criteriaBuilder ->
                val predicateList: MutableList<Predicate> = mutableListOf()
                predicateList.add(
                    criteriaBuilder.equal(
                        root.get<Any>("userId").`as`(String::class.java), userInfo.id
                    )
                )
                if (processQueryPo.processDefinitionKeys != null && processQueryPo.processDefinitionKeys!!.isNotEmpty()) {
                    predicateList.add(
                        root.get<Any>("processDefinitionKey").`as`(String::class.java)
                            .`in`(processQueryPo.processDefinitionKeys)
                    )
                }
                if (!CommonTools.isNullStr(processQueryPo.processBusinessKey)) {
                    predicateList.add(
                        criteriaBuilder.equal(
                            root.get<Any>("businessKey").`as`(String::class.java), processQueryPo.processBusinessKey
                        )
                    )
                }
                if (!CommonTools.isNullStr(processQueryPo.startUserId)) {
                    predicateList.add(
                        criteriaBuilder.equal(
                            root.get<Any>("startUserId").`as`(String::class.java), processQueryPo.startUserId
                        )
                    )
                }
                if (processQueryPo.startTime != null) {
                    predicateList.add(
                        criteriaBuilder.ge(
                            root.get<Any>("startTime").`as`(Long::class.java), processQueryPo.startTime
                        )
                    )
                }
                if (processQueryPo.endTime != null) {
                    predicateList.add(
                        criteriaBuilder.le(
                            root.get<Any>("startTime").`as`(Long::class.java), processQueryPo.endTime
                        )
                    )
                }
                criteriaBuilder.and(*predicateList.toTypedArray())
            }, buildPageRequest(processQueryPo.queryParam!!)).let {
                CustomerQueryPageVo(
                    currPage = processQueryPo.queryParam!!.currPage!!,
                    pageSize = processQueryPo.queryParam!!.pageSize!!,
                    totalElements = it.totalElements,
                    content = it.content.map { instance ->
                        findProcessInstance(instance.processInstanceId)
                    })
            }
        } ?: throw WebException("获取当前登录用户信息失败！")
    } catch (e: Exception) {
        logAdapter.error(e.message, e)
        throw WebException(e.message)
    }

    @Throws(WebException::class)
    fun findHighlightElement(processInstanceId: String): ProcessHighlightElementVo = try {
        historyService.createHistoricProcessInstanceQuery().processInstanceId(processInstanceId).singleResult()
            ?.let { historicProcessInstance ->
                val highlightNodeList = historyService.createHistoricActivityInstanceQuery()
                    .processInstanceId(processInstanceId)
                    .orderByHistoricActivityInstanceStartTime().asc()
                    .list().map { historicActivityInstance ->
                        ProcessHighlightNodeVo(
                            id = historicActivityInstance.activityId,
                            type = historicActivityInstance.activityType,
                            name = historicActivityInstance.activityName,
                            startTime = historicActivityInstance.startTime?.time,
                            endTime = historicActivityInstance.endTime?.time,
                            complete = historicActivityInstance.endTime != null
                        )
                    }.toMutableList()
                val highlightNodeIdList = highlightNodeList.map { it.id }
                ProcessHighlightElementVo(
                    highlightNodeList = highlightNodeList,
                    highlightLineList = repositoryService.getBpmnModelInstance(historicProcessInstance.processDefinitionId)
                        .getModelElementsByType(SequenceFlow::class.java).filter { sequenceFlow ->
                            highlightNodeIdList.contains(sequenceFlow.source?.id) &&
                                    highlightNodeIdList.contains(sequenceFlow.target?.id)
                        }.map { sequenceFlow ->
                            ProcessHighlightLineVo(
                                id = sequenceFlow.id,
                                sourceId = sequenceFlow.source?.id,
                                targetId = sequenceFlow.target?.id
                            )
                        }.toMutableList()
                )
            } ?: throw WebException("获取流程实例失败！")
    } catch (e: Exception) {
        logAdapter.error(e.message, e)
        throw WebException(e.message)
    }
}
